# QuestionType is used to provide functionality to a question based on its 
# question_type.

import re
from random import shuffle
from itertools import chain
from bs4 import BeautifulSoup, Tag
from question.models import Correct, Wrong
from collections import OrderedDict

class QuestionType():
    """
    A generic question type.
    Subclass and override methods to implement specific functionality to 
    different question_types.
    """
    
    def __init__(self, name, answer_partial_url, display_partial_url):
        # A display name for the question_type.
        self.name = name
        # An html partial for creating answers.
        self.answer_partial_url = answer_partial_url
        # An html partial for displaying answers.
        self.display_partial_url = display_partial_url

    def __unicode__(self):
        return self.name

    def get_answers(self, question):
        """
        Return answer(s).
        Override with question_type specific functionality.
        """
        return []
    
    def check_answer(self, question, answer):
        """
        Check a generic question's answer
        """
        correct_answers = Correct.objects.filter(question=question)
        for c in correct_answers:
            if answer == c.answer.answer:
                return True

        return False

class SelectAnswer(QuestionType):
    """
    Represents a question with selectable answer.
    This question provides one or more alternative answers from which the user
    chooses one.
    """

    def get_answers(self, question):
        """
        Return the list of answers for the given question.
        """
        correct_answers = Correct.objects.filter(question=question)
        wrong_answers = Wrong.objects.filter(question=question)

        answers = list(chain(correct_answers, wrong_answers))
        answers = [(a, a.answer.answer) for a in answers]

        shuffle(answers)
        return answers

    def check_answer(self, question, answer):
        """
        Check if the given answer for the given question is correct.
        """
        correct_answers = Correct.objects.filter(question=question)
        for c in correct_answers:
            if int(answer) == c.answer.id:
                return True

        return False

class FreeTextAnswer(QuestionType):
    """
    Represents a free text answer.
    This question allows the user to submit an arbitrary string to correct.
    """

    def check_answer(self, question, answer):
        """
        Check if the given answer for the given question is correct.
        Takes into account case sensitivity if needed.
        """
        correct_answers = Correct.objects.filter(question=question)
        for c in correct_answers:
            if question.is_case_sensitive:
                if answer == c.answer.answer:
                    return True
            else:
                if answer.lower() == c.answer.answer.lower():
                    return True
        return False

# A dict connecting a question's question_type with an appropriate name as well
# as html partials for creating and displaying a question's answers.
# The key corresponds to a question's question_type in the database.
# The dict is constructed using tuples to preserve the order.
QUESTION_TYPES = OrderedDict([
    ('single_select', SelectAnswer('Single select', 
                                      '_answers_single_select.html', 
                                      '_display_single_select.html')),
    ('multiple_select', SelectAnswer('Multiple select', 
                                        '_answers_single_select.html', 
                                        '_display_multiple_select.html')),
    ('text_field', FreeTextAnswer('Text field', 
                                   '_answers_text_field.html', 
                                   '_display_text_field.html')),
    ('text_area', FreeTextAnswer('Text area', 
                                  '_answers_text_area.html', 
                                  '_display_text_area.html')),
    ('radio_button', QuestionType('Radio button', 
                                  '_answers_radio_button.html', 
                                  '_display_radio_button.html')),
])

